package com.bianmaba.spring.supports.data.repository.specification.condition.predicate.generator;

import com.bianmaba.spring.supports.data.repository.specification.condition.param.Param;
import com.bianmaba.spring.supports.data.repository.specification.condition.param.converter.ConverterManager;
import org.apache.commons.lang3.math.NumberUtils;
import org.apache.commons.lang3.time.DateUtils;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.Path;
import javax.persistence.criteria.Predicate;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.text.ParseException;
import java.util.Date;

/**
 * Created by cwx183898 on 2017/12/17.
 */
public abstract class AbstractPredicateGenerator {
    private static final String[] DATE_FORMATERS = new String[]{
            "yyyy-MM", "yyyy-MM-dd", "yyyy-MM-dd HH:mm", "yyyy-MM-dd HH:mm:ss",
            "yyyy-MM-ddTHH:mm", "yyyy-MM-ddTHH:mm:ss",
            "yyyy/MM", "yyyy/MM/dd", "yyyy/MM/dd HH:mm", "yyyy/MM/dd HH:mm:ss",
            "yyyy/MM/ddTHH:mm", "yyyy/MM/ddTHH:mm:ss"};

    public Predicate toPredicate(CriteriaBuilder builder, Path path, Param param) {

        Class clazz = path.getJavaType();
        Object[] values = ConverterManager.get(clazz).transform(param.getValue());
        Predicate predicate = createPredicate(builder, clazz, path, values);
        return predicate;
    }


    protected Predicate createPredicate(CriteriaBuilder builder, Class clazz, Path path, Object[] datas) {
        Method method = getCreateMethod(builder);
        if (method == null) {
            return null;
        }
        Predicate predicate = null;
        if (datas == null || datas.length == 0) {
            predicate = invokeCreate(builder, method, path, null);
        } else {
            for (Object data : datas) {
                if (predicate == null) {
                    predicate = invokeCreate(builder, method, path, data);
                } else {
                    Predicate newPredicate = invokeCreate(builder, method, path, data);
                    predicate = builder.or(predicate, invokeCreate(builder, method, path, data));
                }
            }
        }
        return predicate;
    }

    protected Predicate invokeCreate(CriteriaBuilder builder, Method method, Path path, Object data) {
        Predicate predicate = null;
        try {
            predicate = (Predicate) method.invoke(builder, path, data);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return predicate;
    }

    protected abstract Method getCreateMethod(CriteriaBuilder builder);

    private Date parseDate(String val) {
        try {
            val = val.replaceAll("T", " ");
            return DateUtils.parseDate(val, DATE_FORMATERS);
        } catch (ParseException e) {
            e.printStackTrace();
            return null;
        }
    }


    protected Boolean[] toBooleanArray(Object[] values) {
        Boolean[] datas = new Boolean[values.length];
        for (int i = 0; i < values.length; i++) {
            datas[i] = Boolean.parseBoolean(values[i].toString());
        }
        return datas;
    }

    protected Date[] toDateArray(Object[] values) {
        Date[] datas = new Date[values.length];
        for (int i = 0; i < values.length; i++) {
            datas[i] = parseDate(values[i].toString());
        }
        return datas;
    }

    protected String[] toStringArray(Object[] values) {
        String[] datas = new String[values.length];
        for (int i = 0; i < values.length; i++) {
            datas[i] = values[i].toString();
        }
        return datas;
    }

    protected Number[] toNumberArray(Object[] values) {
        Number[] datas = new Number[values.length];
        for (int i = 0; i < values.length; i++) {
            datas[i] = NumberUtils.createNumber(values[i].toString());
        }
        return datas;
    }
}